﻿using MvvmCross.Core.Parse.StringDictionary;
using MvvmCross.Core.ViewModels;
using MvvmCross.Platform;
using MvvmCross.Uwp.Platform;
using Sidvall.Serialization;
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace Sidvall.UI.Uwp
{
    public abstract class ApplicationManager
    {
        #region Public Members

        #region OnLaunchedAsync

        public async Task OnLaunchedAsync(Window window, Frame rootFrame, LaunchActivatedEventArgs e)
        {
            if (Sidvall.UI.SystemContext.Current.ApplicationManager.Status == ApplicationStatus.NotInitialized)
                await DoSetupAsync(window, rootFrame, e);
        }

        #endregion

        // Protected
        #region RunAppStart

        protected virtual bool RunAppStart
        {
            get
            {
                return true;
            }
        }

        #endregion
        protected abstract void RegisterTypes();
        protected abstract MvxWindowsSetup CreateSetup(Frame rootFrame, LaunchActivatedEventArgs e);
        #region GetMaxHistoryItemCount

        protected virtual int? GetMaxHistoryItemCount()
        {
            return 10;
        }

        #endregion
        #region OnInitialized

        protected virtual void OnInitialized()
        {
        }

        #endregion
        #region OnStarted

        protected virtual void OnStarted()
        {
        }

        #endregion

        #endregion
        #region Private Members

        #region ApplicationUnhandledException

        private async void ApplicationUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
        {
            await HandleExceptionAsync(e.Exception);
        }

        #endregion
        #region DoSetupAsync

        private async Task DoSetupAsync(Window window, Frame rootFrame, LaunchActivatedEventArgs e)
        {
            // Preinitialize
            try
            {
                Windows.UI.Xaml.Application.Current.UnhandledException += ApplicationUnhandledException;
                RegisterTypes();
            }
            catch (System.Exception ex)
            {
                await ShowDialogAsync(GetErrorMessage(ex), "Exception - Preinitialize");
                Windows.UI.Xaml.Application.Current.Exit();
            }

            // Initializing
            try
            {
                Sidvall.UI.SystemContext.Current.ApplicationManager.Status = Sidvall.UI.ApplicationStatus.Initializing;
                Sidvall.UI.Uwp.SystemContext.Current.MainWindow = window;
                Sidvall.UI.Uwp.SystemContext.Current.RootPage = window.Content as Page;
                Sidvall.UI.Uwp.SystemContext.Current.RootFrame = rootFrame;
                Sidvall.UI.Uwp.SystemContext.Current.RootFrame.NavigationFailed += OnNavigationFailed;
                Sidvall.UI.Uwp.SystemContext.Current.RootFrame.Navigated += OnNavigated;
                Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;

                Sidvall.UI.SystemContext.Current.NavigationManager.MaxHistoryItemCount = GetMaxHistoryItemCount();
                var setup = CreateSetup(rootFrame, e);
                if (setup != null)
                {
                    setup.Initialize();
                    Sidvall.UI.EventManager.IsEnabled = true;
                }
                OnInitialized();
            }
            catch (System.Exception ex)
            {
                await ShowDialogAsync(GetErrorMessage(ex), "Exception - Initializing");
                Windows.UI.Xaml.Application.Current.Exit();
            }

            // Starting
            try
            {
                Sidvall.UI.SystemContext.Current.ApplicationManager.Status = Sidvall.UI.ApplicationStatus.Starting;
                if (this.RunAppStart)
                {
                    var start = Mvx.Resolve<IMvxAppStart>();
                    start.Start();
                }
                OnStarted();
                Sidvall.UI.SystemContext.Current.ApplicationManager.SetupCompleted = true;
            }
            catch (System.Exception ex)
            {
                await ShowDialogAsync(GetErrorMessage(ex), "Exception - Starting");
                Windows.UI.Xaml.Application.Current.Exit();
            }
        }

        #endregion
        #region GetErrorMessage

        private static string GetErrorMessage(System.Exception exception)
        {
            return GetErrorMessage(exception, System.Environment.NewLine, System.Environment.NewLine + System.Environment.NewLine);
        }
        private static string GetErrorMessage(System.Exception exception, string messageSeparator, string sectionSeparator)
        {
            var manager = new Sidvall.ExceptionHandling.SystemExceptionProvider();
            return manager.GetMessage(new System.Exception[] { exception }, messageSeparator, sectionSeparator);
        }

        #endregion
        #region HandleExceptionAsync

        private async Task HandleExceptionAsync(System.Exception ex)
        {
            if (ex == null)
                return;

            try
            {
                await LogAsync(ex);
                await Sidvall.UI.SystemContext.Current.DialogManager.ShowDialogErrorMessageAsync(ex);
            }
            catch
            {
            }
        }

        #endregion
        #region OnBackRequested

        private void OnBackRequested(object sender, Windows.UI.Core.BackRequestedEventArgs e)
        {
            var rootFrame = Sidvall.UI.Uwp.SystemContext.Current.RootFrame;
            if (rootFrame.CanGoBack)
            {
                e.Handled = true;
                rootFrame.GoBack();
            }
        }

        #endregion
        #region OnNavigationFailed

        private void OnNavigationFailed(object sender, Windows.UI.Xaml.Navigation.NavigationFailedEventArgs e)
        {
            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
        }

        #endregion
        #region OnNavigated

        private void OnNavigated(object sender, NavigationEventArgs e)
        {
            var iMax = Sidvall.UI.Uwp.SystemContext.Current.RootFrame.BackStack.Count;
            if (iMax > 0)
            {
                var parser = new MvxStringDictionaryParser();
                for (int i = iMax - 1; i >= 0; i--)
                {
                    var pageStackEntry = Sidvall.UI.Uwp.SystemContext.Current.RootFrame.BackStack[i];
                    var parameters = parser.Parse(pageStackEntry.Parameter.ToString(null));
                    if (parameters.ContainsKey("Pres"))
                    {
                        parameters = parser.Parse(parameters["Pres"]);
                        if (parameters.ContainsKey(Sidvall.UI.Navigation.NavigationManager.AddToHistoryParameter))
                        {
                            var p = parameters[Sidvall.UI.Navigation.NavigationManager.AddToHistoryParameter];
                            var addToHistory = p.ToBoolean(true);
                            if (!addToHistory)
                            {
                                Sidvall.UI.Uwp.SystemContext.Current.RootFrame.BackStack.RemoveAt(i);
                            }
                        }
                    }
                }
            }

            // Each time a navigation event occurs, update the Back button's visibility
            Windows.UI.Core.SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = ((Frame)sender).CanGoBack ?
                Windows.UI.Core.AppViewBackButtonVisibility.Visible :
                Windows.UI.Core.AppViewBackButtonVisibility.Collapsed;
        }

        #endregion
        #region ShowDialogAsync

        private async Task ShowDialogAsync(string content, string title)
        {
            var dialog = new Windows.UI.Popups.MessageDialog(content, "Unhandled Exception");
            await dialog.ShowAsync();
        }

        #endregion
        #region LogAsync

        private async Task LogAsync(System.Exception ex)
        {
            if (Sidvall.SystemContext.Current.LogManager != null)
                await Sidvall.SystemContext.Current.LogManager.WriteAsync(ex, Sidvall.Logging.LogCategories.Application);
        }

        #endregion

        #endregion
    }
}
